#ifndef UnstructMG_GLOBAL_LU_HPP
#define UnstructMG_GLOBAL_LU_HPP

#include "par_CSRMatrix.hpp"
#include "direct_solver.hpp"

/*  目前采用的方法是
    每个进程各自组装好自己负责的行（包括不同block的不同box，以及这些行内的inter-block非结构非零元），
    组成一个局部的CSR矩阵
    本进程一共负责了 n 行 （即所有block所有box加起来一共有 n 个网格单元），总共有 nnz 个非零元
    loc_row_ptr: 长度 n + 1
    loc_col_idx：长度 nnz
    loc_vals   : 长度 nnz
    以及
    my_row_idx : 长度 n，记录我负责的每一行的全局行序号

    然后全局的0号进程做 Gather
    全局总共有多少行自己可以立即知道（通过ssgrid.glb_blk_info）
    第一次 Gather : 获知每个进程要发多少行，和多少个非零元
    第二次 Gatherv: 从每个进程处收取，所有东西打包一起通信

 */

// 完全LU类中的data_t类型仅做摆设，实际存储和计算都用calc_t
template<typename idx_t, typename data_t, typename setup_t, typename calc_t, int dof>
class GlobalLU final : public Solver<idx_t, setup_t, calc_t, dof> {
public:
    int root_pid;// 负责Gather(v)和Scatter(v)的进程号
    idx_t my_nrows = 0;
    idx_t glb_nrows = 0;
    GlobalLU();
    ~GlobalLU();
    void SetOperator(const Operator<idx_t, setup_t, dof> & op);
// private:

    int * displs = nullptr, * recvcnts = nullptr;
    // 向量的发送和接收缓冲区，长度为my_nrows
    calc_t * vec_buf = nullptr;
    // 全局的右端项b和解向量x（只有root进程开辟）
    calc_t * global_b = nullptr, * global_x = nullptr;
    // 记录每个进程所负责的行数和存储行序号的起始位置
    idx_t * proc_map = nullptr;// [0, glb_np]
    // proc_map[pid] ~ proc_map[pid+1] 记录了第pid个进程所负责的行的全局序号
    idx_t * proc_row_ids = nullptr;// [0, glb_nrows)

    CSR_sparseMat<idx_t, calc_t, calc_t> * glbA_csr = nullptr;

protected:
    void truncate();
    void Mult_ipl(const par_Vector<idx_t, calc_t, dof> & input, 
                        par_Vector<idx_t, calc_t, dof> & output) const ;
};
typedef GlobalLU<int, double, double, double, 1> GlobalLU_I32All64_DOF1;
typedef GlobalLU<int, double, double, double, 2> GlobalLU_I32All64_DOF2;
typedef GlobalLU<int, double, double, double, 3> GlobalLU_I32All64_DOF3;
typedef GlobalLU<int, double, double, double, 4> GlobalLU_I32All64_DOF4;

typedef GlobalLU<int, float , double, double, 1> GlobalLU_I32K64P64D32_DOF1;
typedef GlobalLU<int, float , double, double, 2> GlobalLU_I32K64P64D32_DOF2;
typedef GlobalLU<int, float , double, double, 3> GlobalLU_I32K64P64D32_DOF3;
typedef GlobalLU<int, float , double, double, 4> GlobalLU_I32K64P64D32_DOF4;

typedef GlobalLU<int, float , double, float , 1> GlobalLU_I32K64P32D32_DOF1;
typedef GlobalLU<int, float , double, float , 2> GlobalLU_I32K64P32D32_DOF2;
typedef GlobalLU<int, float , double, float , 3> GlobalLU_I32K64P32D32_DOF3;
typedef GlobalLU<int, float , double, float , 4> GlobalLU_I32K64P32D32_DOF4;

typedef GlobalLU<int, float , float , float , 1> GlobalLU_I32All32_DOF1;
typedef GlobalLU<int, float , float , float , 2> GlobalLU_I32All32_DOF2;
typedef GlobalLU<int, float , float , float , 3> GlobalLU_I32All32_DOF3;
typedef GlobalLU<int, float , float , float , 4> GlobalLU_I32All32_DOF4;
#ifdef USE_FP16
typedef GlobalLU<int, __fp16, double, float , 1> GlobalLU_I32K64P32D16_DOF1;
typedef GlobalLU<int, __fp16, double, float , 2> GlobalLU_I32K64P32D16_DOF2;
typedef GlobalLU<int, __fp16, double, float , 3> GlobalLU_I32K64P32D16_DOF3;
typedef GlobalLU<int, __fp16, double, float , 4> GlobalLU_I32K64P32D16_DOF4;

typedef GlobalLU<int, __fp16, float , float , 1> GlobalLU_I32K32P32D16_DOF1;
typedef GlobalLU<int, __fp16, float , float , 2> GlobalLU_I32K32P32D16_DOF2;
typedef GlobalLU<int, __fp16, float , float , 3> GlobalLU_I32K32P32D16_DOF3;
typedef GlobalLU<int, __fp16, float , float , 4> GlobalLU_I32K32P32D16_DOF4;
#endif
#endif